/*//GEN-LINE:initComponents
 * TreePanel.java
 *
 * Created on den 4 maj 2007, 14:41
 */
package grex.Graphics;


import grex.fitnessfunctions.ErrorFunctions.IErrorFunction;
import grex.ErrorManagement.EvolutionListener;
import grex.EvolutionProcess;
import grex.GP;
import grex.Options;
import grex.SimplificationListener;
import grex.Simplyfier;
import grex.fitnessfunctions.ErrorFunctions.ErrorCalculator;
import grex.Nodes.Node;
import grex.Nodes.GeneException;

import grex.Nodes.If;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TreePanel extends javax.swing.JPanel implements EvolutionListener, SimplificationListener, ActionListener {

    private int width,  height;
    private GP gp;
    private GUINode headGUINode;
    private String title;
    private int xStep,  yStep,  xMargin = 120;
    private NumberFormat nf = NumberFormat.getInstance();
    private boolean modified = false;
    private double avgFitness,  avgLength;
    private ActionListener listener;
    private GUINode lastClickedGUINode;
    private ErrorCalculator errorCalculator;
    public TreePanel(GP gp, String title,ErrorCalculator ec) {
        initComponents();
        setBackground(new java.awt.Color(227, 227, 227));
        setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));        
        errorCalculator = ec;
        nf.setMaximumFractionDigits(2);
        this.title = title;
        if (gp != null) {
            this.gp = gp;
            this.gp.updateNodeLevels();
            headGUINode = makeTree(gp);
            this.repaint();
           
        }
    }

    public void setActionListener(ActionListener listener) {
        this.listener = listener;
        initActionListener();
    }

    public void initActionListener() {
        if (listener != null) {
            Component c;
            for (int i = 0; i < getComponentCount(); i++) {
                c = getComponent(i);
                if (c instanceof GUINode) {
                    GUINode n = ((GUINode) c);
                    n.addActionListener(listener);
                    n.addActionListener(this);
                }
            }
        }
    }

    public void evolutionStarted(EvolutionProcess source) {
        title = "Evolving";
    }

    public void batchTerminated(EvolutionProcess source) {
            setNewGP(source.getBatchWinner());
            avgFitness = source.getAverageFitness();
            avgLength = source.getAverageLength();
            this.repaint();
    }

    double percentPerformed = 0;
    public void generationTerminated(EvolutionProcess source) {
     //   if (gp == null || gp.getFitness() > source.getBestGP().getFitness()) {
            setNewGP(source.getBestGP());
            avgFitness = source.getAverageFitness();
            avgLength = source.getAverageLength();
            Options ops = source.getOptions();
            percentPerformed = ((double) (source.getCurrentBatch()*ops.getGENERATIONS() + source.getCurrentGeneration()))/ops.getGENERATIONS()*ops.getBATCH_SIZE();
            try{
            this.repaint();
            }catch (Exception e){
                System.out.println("Fel i utritningen av Tree panel");
            }
       // }

    }

    public void evolutionTerminated(EvolutionProcess source) {
        title = "Evolution completed";

    }

    public void evolutionProcessTerminated(EvolutionProcess source) {
        source.removeEvolutionLister(this);
    }

    public void simplificationStarted(Simplyfier source) {
        setNewGP(source.getSimplyfiedGP());
        avgFitness = source.getAverageFitness();
        avgLength = source.getAverageLength();
        title = "Simplifying";
        this.repaint();
    }

    public GP getGP() {
        return gp;
    }

    public void simplificationGenerationTerminated(Simplyfier source) {
        setNewGP(source.getSimplyfiedGP());
        avgFitness = source.getAverageFitness();
        avgLength = source.getAverageLength();
        this.repaint();
    }

    public void simplifictionTerminated(Simplyfier source) {
        title = "Simplification completed";
    }
    int currentLeaf = 0;

    public GUINode createTree(Node gene, GUINode parent, int depth) {
        int x, y, parentX = 0;
        Node[] children = gene.getChildren();
        GUINode node = new GUINode(gene, this, gp);

        FontMetrics f = getFontMetrics(node.getFont());
        if (children != null) {
            GUINode[] nodeChildren;
            if (gene instanceof If) {
                nodeChildren = new GUINode[2];
            } else {
                nodeChildren = new GUINode[children.length];
            }
            int j = 0;
            for (int i = 0; i < children.length; i++) {
                if (!(gene instanceof If && i == 0)) {
                    nodeChildren[j] = createTree(children[i], node, depth + 1);
                    j++;
                }
            }
            int w = 0;
            for (int k = 0; k < nodeChildren.length; k++) {
                parentX += nodeChildren[k].getIx() / nodeChildren.length;
                w += f.stringWidth(nodeChildren[k].getText()) / nodeChildren.length;
            }
            y = yStep * depth;//gene.getNodeLevel();
            x = parentX + 8 + w / 2 - f.stringWidth(node.getText()) / 2;
            node.setNodeChildren(nodeChildren);
            node.setIx(x);
            add(node, new org.netbeans.lib.awtextra.AbsoluteConstraints(x, y, -1, -1));
            return node;
        }

        y = yStep * gene.getNodeLevel();
        x = xMargin + xStep * currentLeaf;//-f.stringWidth(node.getText())/2-8;
        node.setIx(x);
        add(node, new org.netbeans.lib.awtextra.AbsoluteConstraints(x, y, -1, -1));
        currentLeaf++;
        return node;
    }

    public  void update() {
        if (gp != null) {
            setNewGP(gp);
            initActionListener();
            this.repaint();
        }
    }

    private GUINode makeTree(GP gp) {
        try {
            //if(gp.getOptions().getPROBLEM_TYPE()==Options.CLASSIFICATION)
            currentLeaf = 0;
            xStep = (this.getWidth() - xMargin - gp.getNrOfLeaves() * 10) / (gp.getNrOfLeaves());
            yStep = this.getHeight() / (gp.calcDepth() + 1);
            if (gp.calcDepth() == 1) {
                yStep = yStep / 2;
            }
        } catch (Exception e) {
            xStep = 600 / (gp.getNrOfLeaves());
            yStep = 300 / gp.calcDepth();
        }
        return createTree(gp.getHead(), null, 1);
    }

    public  void setNewGP(GP newGP) {
       // System.out.println(newGP.getFormatedTree());
        this.gp = (GP) newGP.clone();
        //gp.train();

        this.gp.updateNodeLevels();
        this.removeAll();
        headGUINode = makeTree(gp);
        if(gp.getOptions().getGp() != null)
            markUniqueSplits(headGUINode);
        initActionListener();
        this.validate();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">                          
    private void initComponents() {

        setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

    }// </editor-fold>                        
    // Variables declaration - do not modify                     
    // End of variables declaration                   
    public void drawGUINodeLine(Graphics g, GUINode node) {
        int x1, x2, y1, y2;
        GUINode[] childs = null;
        if (node != null) {
            childs = node.getNodeChildren();
        }
        if (childs != null) {
            for (int i = 0; i < childs.length; i++) {
                x1 = node.getX() + node.getWidth() / 2;
                y1 = node.getY() + node.getHeight();
                x2 = childs[i].getX() + childs[i].getWidth() / 2;
                y2 = childs[i].getY();
                g.setColor(Color.black);
                g.drawLine(x1, y1, x2, y2);

                drawGUINodeLine(g, childs[i]);
            }
        }
    }

    @Override
    public  void paintComponent(Graphics g) {
        super.paintComponent(g);


        g.setColor(Color.white);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        g.setColor(Color.BLACK);
        if (gp != null) {
            if (title != null) {
                Font f = g.getFont();
                FontMetrics fm = g.getFontMetrics();
                int w = fm.stringWidth(title);

                g.setFont(new Font("SansSerif", Font.BOLD, 15));
                g.drawString(title, this.getWidth() / 2 - w / 2, 30);
                g.setFont(f);
            }

            drawGUINodeLine(g, headGUINode);

           //try {
         //       String tmp = "";
                //g.drawString(gp.get)
                // gp.train();
                g.drawString("Avg fitness: ", 10, 20);
                g.drawString(nf.format(avgFitness), 85, 20);
                g.drawString("Avg length: ", 10, 40);
                g.drawString(nf.format(avgLength), 85, 40);
                g.drawString("Best fitness: ", 10, 60);
                g.drawString(nf.format(gp.getFitness()), 85, 60);
                g.drawString("Length: ", 10, 80);
                g.drawString(nf.format(gp.getNrOfNodes()), 85, 80);
                List<IErrorFunction> functions;
                if (gp.getOptions().getPROBLEM_TYPE() == Options.CLASSIFICATION){
                        functions = errorCalculator.getClassificationFunctions();
                }else{
                        functions = errorCalculator.getRegressionFunctions();
                }
                int y=110;
                for(IErrorFunction f: functions){
                    g.drawString("Train " + f.getName() +": ", 10, y);
                    g.drawString(nf.format(gp.calcTrainError(f.getName())), 85, y);
                    y+=20;
                }
                y+=10;
                /*if(gp.getPcVal().values().size()>0){
                    for(IErrorFunction f: functions){
                        g.drawString("Val " + f.getName() +": ", 10, y);
                        g.drawString(nf.format(gp.calcValError(f.getName())), 85, y);
                        y+=20;
                    }
                    y+=10;
                }*/
                for(IErrorFunction f: functions){
                    g.drawString("Test " + f.getName() +": ", 10, y);
                    g.drawString(nf.format(gp.calcTestError(f.getName())), 85, y);
                    y+=20;
                }//*/
/*
                    g.drawString("Train ACC: ", 10, 110);
                    g.drawString(nf.format(gp.getTrainAcc()), 85, 110);
                    g.drawString("Train AUC: ", 10, 130);
                    g.drawString(nf.format(gp.getTrainAUC()), 85, 130);
                    g.drawString("Train BRI: ", 10, 150);
                    g.drawString(nf.format(gp.getTrainBRIER()), 85, 150);
                    g.drawString("Val ACC: ", 10, 180);
                    g.drawString(nf.format(gp.getValAcc()), 85, 180);
                    g.drawString("Val AUC: ", 10, 200);
                    g.drawString(nf.format(gp.getValAUC()), 85, 200);
                    g.drawString("Val BRI: ", 10, 220);
                    g.drawString(nf.format(gp.getValBRIER()), 85, 220);
                    g.drawString("Test ACC: ", 10, 250);
                    g.drawString(nf.format(gp.getTestAcc()), 85, 250);
                    g.drawString("Test AUC: ", 10, 270);
                    g.drawString(nf.format(gp.getTestAUC()), 85, 270);
                    g.drawString("Test BRI: ", 10, 290);
                    g.drawString(nf.format(gp.getTestBRIER()), 85, 290);
                } else {//Regression
                    
                    g.drawString("Train RMSE: ", 10, 110);
                    g.drawString(nf.format(gp.calcTestError("RMSE")),85,110);
                    g.drawString("Train MUAPE: ", 10, 130);
                    g.drawString(nf.format(muape.calcMUAPE(gp, gp.getPcTrain())), 85, 130);
                    g.drawString("Train GMRAE: ", 10, 150);
                    g.drawString(nf.format(gmrae.calcGMRAE(gp,gp.getPcTrain())), 85, 150);
                    g.drawString("Val RMSE: ", 10, 180);
                    g.drawString(nf.format(gp.calcValError("RMSE")), 85, 180);
                    g.drawString("Val MUAPE: ", 10, 200);
                    g.drawString(nf.format(muape.calcMUAPE(gp, gp.getPcVal())), 85, 200);
                    g.drawString("Val GMRAE: ", 10, 220);
                    g.drawString(nf.format(gmrae.calcGMRAE(gp,gp.getPcVal())), 85, 220);
                    g.drawString("Test RMSE: ", 10, 250);
                    g.drawString(nf.format(gp.calcTestError("RMSE")), 85, 250);
                    g.drawString("Test MUAPE: ", 10, 270);
                    g.drawString(nf.format(muape.calcMUAPE(gp, gp.getPcTest())), 85, 270);
                    g.drawString("Test GMRAE: ", 10, 290);
                    g.drawString(nf.format(gmrae.calcGMRAE(gp,gp.getPcTest())), 85, 290);
                }
              //  g.drawString(tmp, 1, 20);
            } catch (GeneException ex) {
                ex.printStackTrace();
            }//*/

        }

    }

    public boolean isModified() {
        return modified;
    }

    public void setModified(boolean modified) {
        this.modified = modified;
    }
    
    ArrayList <Node> splits,catSplits;
    public void markUniqueSplits(GUINode node){
        if(splits==null){

            /*Options ops = gp.getOptions();
            splits = ops.getConSplits();
            catSplits = ops.getCatSplits();*/
            splits = new ArrayList<Node>();
            initLikness(gp.getOptions().getGp().getHead());
        }
        Node g = node.getGene();
        if(g instanceof If)
            g = g.getChildren()[0];

        if(!splits.contains(g))
            node.setForeground(Color.red);

        GUINode[] children = node.getNodeChildren();
        if(children != null){
            markUniqueSplits(children[0]);
            markUniqueSplits(children[1]);
        }
                
    }

    private void initLikness(Node g){
        if(g instanceof If){
            Node split = g.getChildren()[0];            
            splits.add(split);
            initLikness(g.getChildren()[1]);
            initLikness(g.getChildren()[2]);
        }else
            splits.add(g);

    }

    public void actionPerformed(ActionEvent e) {
        GUINode n = (GUINode) e.getSource();
        if (lastClickedGUINode != null) {
            lastClickedGUINode.setBackground(n.getBackground());
            lastClickedGUINode.setEnabled(true);
        }
        if (n.getNodeChildren() == null) {
            n.setBackground(Color.YELLOW);
            n.setEnabled(false);
            lastClickedGUINode = n;
        }
    }
}
